DEFINE CLASS ConvertTime AS CUSTOM
  * Converts to and from FILETIME to SYSTEMTIME
  * structures

  * Defined constants

  #DEFINE WORD 2
  #DEFINE LDWORD 8

  oInteger = ""

  PROCEDURE Init
  
    SET PROCEDURE TO CON_INT ADDITIVE
    This.oInteger = CREATEOBJECT("ConvertInt")
    RELEASE PROCEDURE CON_INT
    RETURN NOT ISNULL(This.oInteger)
  ENDPROC
  
  FUNCTION FileToLocalTime
    * Converts a FILETIME structure
    * from the system time (UTC) to the 
    * local time
    
    LPARAMETER pcFileTime
    * A FILETIME structure
    
    DECLARE SHORT FileTimeToLocalFileTime IN Win32API;
      STRING @lpFileTime, STRING @lpLocalFileTime
    LOCAL lcresult, lcFileTime
    lcFileTime = pcFileTime
    lcresult = REPLICATE(CHR(0), LDWORD)
    = FileTimeToLocalFileTime(@lcFileTime, @lcresult)
    RETURN lcresult
  ENDFUNC
  
  FUNCTION LocalToFileTime
    * Converts a local FILETIME structure
    * a UTC FILETIME structure
    
    LPARAMETER pcFileTime
    
    DECLARE SHORT LocalFileTimeToFileTime IN Win32API;
      STRING @lpLocalFileTime, STRING @lpFileTime
    LOCAL lcresult, lcfiletime
    lcfiletime = pcFileTime
    lcresult = REPLICATE(CHR(0), LDWORD)
    = LocalFileTimeToFileTime(@lcfiletime, @lcresult)
    RETURN lcresult
  ENDFUNC
  
  FUNCTION SystemToFileTime
  
    LPARAMETER pdtvalue
    * Parameter is of type DATETIME
    DECLARE SHORT SystemTimeToFileTime IN Win32API;
      STRING @lpst,	STRING @lpft
    LOCAL lcsystemtime, lcfiletime
    lcfiletime = REPLICATE(CHR(0), LDWORD)
    lcsystemtime = This.CreateSystemTime(pdtvalue)
    = SystemTimeToFileTime(@lcsystemtime, @lcfiletime)
    RETURN lcfiletime
  ENDFUNC
  
  FUNCTION FileTimeToSystem
    * Converts a FILETIME structure
    * to a SYSTEMTIME structure
    
    LPARAMETER pcFileTime
    
    DECLARE SHORT FileTimeToSystemTime IN Win32API;
      STRING @lpFileTime, STRING @lpSystemTime
    LOCAL lcfiletime, lcresult
    lcfiletime = pcFileTime
    lcresult = REPLICATE(CHR(0), 16)
    = FileTimeToSystemTime(@lcfiletime, @lcresult)
    RETURN lcresult
  ENDFUNC
  
  FUNCTION CreateSystemTime
  
    LPARAMETER pdtvalue
    * Parameter is of type DATETIME
    
    LOCAL lcresult, lnyear, lnmonth,;
    lndow, lnday, lnhour, lnminute, lnse
    lnyear = YEAR(pdtvalue)
    lnmonth = MONTH(pdtvalue)
    * Windows DOWs are zero based, VFP's 1 based
    lndow = DOW(pdtvalue) - 1
    lnday = DAY(pdtvalue)
    lnhour = HOUR(pdtvalue)
    lnminute = MINUTE(pdtvalue)
    lnsec = SEC(pdtvalue)
    WITH This.oInteger
      lcresult = .IntegerToString(lnyear, WORD) +;
        .IntegerToString(lnmonth, WORD) +;
        .IntegerToString(lndow, WORD) +;
        .IntegerToString(lnday, WORD) +;
        .IntegerToString(lnhour, WORD) +;
        .IntegerToString(lnminute, WORD) +;
        .IntegerToString(lnsec, WORD) +;
        .IntegerToString(0, WORD)
    ENDWITH
    RETURN lcresult
  ENDFUNC
  
  FUNCTION SystemToDateTime
  
    LPARAMETER pcsystemtime
    * Parameter is a SYSTEMTIME structure
    
    LOCAL ldtresult, lvyear, lvmonth,;
      lvday, lvhour, lvmin, lvsec
    WITH This.oInteger
      #IF NOT "06.00." $ VERSION()
        lvyear = STR(.StringToInteger(LEFT(pcsystemtime, WORD)))
        lvmonth = STR(.StringToInteger(SUBSTR(pcsystemtime, 3, WORD)))
        lvday = STR(.StringToInteger(SUBSTR(pcsystemtime, 7, WORD)))
        lvhour = STR(.StringToInteger(SUBSTR(pcsystemtime, 9, WORD)))
        lvmin = STR(.StringToInteger(SUBSTR(pcsystemtime, 11, WORD)))
        lvsec = STR(.StringToInteger(SUBSTR(pcsystemtime, 13, WORD)))
        ldtresult = CTOT("^" + lvyear + "/" +;
          lvmonth + "/" + lvday + "," + lvhour +;
          ":" + lvmin + ":" + lvsec)
      #ELSE
        lvyear = .StringToInteger(LEFT(pcsystemtime, WORD))
        lvmonth = .StringToInteger(SUBSTR(pcsystemtime, 3, WORD))
        lvday = .StringToInteger(SUBSTR(pcsystemtime, 7, WORD))
        lvhour = .StringToInteger(SUBSTR(pcsystemtime, 9, WORD))
        lvmin = .StringToInteger(SUBSTR(pcsystemtime, 11, WORD))
        lvsec = .StringToInteger(SUBSTR(pcsystemtime, 13, WORD))
        ldtresult = DATETIME(lvyear, lvmonth, lvday, lvhour, lvmin, lvsec)
      #ENDIF
    ENDWITH
    RETURN ldtresult
  ENDFUNC

  
  FUNCTION ConvertFromFileTime
  
    LPARAMETER pcfiletime
    * A file's FILETIME structure
    * in UTC format
    
    LOCAL lcfiletime, ldtresult, lcsystemtime
    lcfiletime = This.FileToLocalTime(pcfiletime)
    lcsystemtime = This.FileTimeToSystem(lcfiletime)
    ldtresult = This.SystemToDateTime(lcsystemtime)
    RETURN ldtresult
  ENDFUNC
  
  FUNCTION ConvertToFileTime
    * Converts a DATETIME to a UTC FILETIME
    * structure
    
    LPARAMETER pdtDateTime
    
    LOCAL lcsystemtime, lcresult
    lcsystemtime = This.CreateSystemTime(pdtDateTime)
    lcresult = This.SystemToFileTime(lcsystemtime)
    lcresult = This.LocalToFileTime(lcresult)
    RETURN lcresult
  ENDFUNC
  
  FUNCTION Get_Bias
  
    LPARAMETER ttdatetime
    
    DECLARE INTEGER GetTimeZoneInformation IN Win32API;
      STRING @lpTimeZoneInformation
    LOCAL lnresult, lcTimeZone, ltdaylight, ltstandard,;
      lldtdaylight, lldtcurrent, lcdaysystime, lcstdsystime,;
      lncurrent, lnstandard
    lnresult = 0
    lctimezone = REPLICATE(CHR(0), 172)
    = GetTimeZoneInformation(@lctimezone)
    lcstdsystime = SUBSTR(lctimezone, 69, 16)
    lcdaysystime = SUBSTR(lctimezone, 153, 16)
    * Get the starting time for daylight savings
    ltdaylight = This.Date_Calc(lcdaysystime, ttdatetime)
    * Get the starting time for standard
    ltstandard = This.Date_Calc(lcstdsystime, ttdatetime)
    * Is the passed date/time daylight savings?
    lldtdaylight = BETWEEN(ttdatetime, ltdaylight, ltstandard)
    * Determine if an adjustment is necessary
    * Current bias
    lncurrent = This.oInteger.StringToInteger(LEFT(lctimezone, 4))
    * Standard bias
    lnstandard = This.oInteger.StringToInteger(SUBSTR(lctimezone, 85, 4))
    * Is it currently daylight savings?
    lldtcurrent = (lnstandard # lncurrent) AND (lnstandard # 0)
    IF lldtdaylight AND NOT lldtcurrent
      * This compensates for a date/time that's during
      * daylight savings when the current period is
      * standard time
      lnresult = This.oInteger.StringToInteger(RIGHT(lctimezone, 4), .T.)
    ELSE
      IF NOT lldtdaylight AND lldtcurrent
        * This compensates for a date/time that's
        * standard time during daylight savings
        lnresult = ABS(This.oInteger.StringToInteger(RIGHT(lctimezone, 4), .T.))
      ENDIF
    ENDIF
    RETURN lnresult
  ENDFUNC
  
  FUNCTION Date_Calc
    * Calculates the date of a time
    * change from Daylight or Standard time
    
    LPARAMETERS tcSysTime, ttdatetime
    
    LOCAL ltresult, lnyear, lndow, lnday,;
      lndow, lddate
    * Year of the change
    lnyear = YEAR(ttdatetime)
    * The month of the change
    lnmonth = This.oInteger.StringToInteger(SUBSTR(tcSysTime, 3, 2))
    * Window's DOWs are zero based, VFP's 1 based
    lndow = This.oInteger.StringToInteger(SUBSTR(tcSysTime, 5, 2)) + 1
    * Get the day - 1 or 5.
    * 1 = First occurance of the DOW
    * 5 = Last occurance of the DOW
    lnday = This.oInteger.StringToInteger(SUBSTR(tcSysTime, 7, 2))
    lddate = This.AdjustDate(lnmonth, lndow, lnday, lnyear)
    * Create date/time
    ltresult = DATETIME(lnyear, lnmonth, DAY(lddate),;
      This.oInteger.StringToInteger(SUBSTR(tcSysTime, 9, 2)))
    RETURN ltresult
  ENDFUNC
  
  FUNCTION AdjustDate
  
    LPARAMETERS tnmonth, tndow, tnday, tnyear
    
    LOCAL ldresult, lddate
    * Get the first day of the month
    lddate = DATE(tnyear, tnmonth, 1)
    lllast = (tnday = 5)
    IF NOT lllast
      IF DOW(lddate) = tndow
        ldresult = lddate
      ELSE
        ldresult = lddate + (7 * tnday) - (DOW(lddate) - 1)
      ENDIF
    ELSE
      * Get the last date of the month
      lddate = GOMONTH(lddate, 1) - 1
      IF DOW(lddate) = tndow
        ldresult = lddate
      ELSE
        ldresult = lddate - (DOW(lddate) - 1)
      ENDIF
    ENDIF
    RETURN ldresult
  ENDFUNC
  
  PROCEDURE Destroy

    This.oInteger = .NULL.
    RETURN
  ENDPROC

ENDDEFINE